import React, { useCallback, useContext, useEffect, useRef, useState } from 'react'
import {
  Button,
  Col,
  Divider,
  Dropdown,
  Form,
  Icon,
  Input,
  Menu,
  Modal,
  Row,
  Tooltip,
  Typography,
  Upload,
  message,
} from 'antd'
import { useDeepCompareEffect, useRequest } from 'ahooks'
import isPlainObject from 'lodash/isPlainObject'
import Tree, { FilteringContainer, renderers } from 'react-virtualized-tree'
import last from 'lodash/last'
import classnames from 'classnames'
import get from 'lodash/get'
import isEqual from 'lodash/isEqual'

import { isString } from 'lodash'
import { TreeFilter } from '../NodePathSelect/utils'
import { validationResult } from '../SourcePanel/SourceEditor'
import Context from './targetContext'
import { getAttrPath, getNodeById, hasRules, serialize } from './utils'
import Node from './components/Node'
import styles from './styles.less'
import { default as TransformerContext } from '@/pages/Transformer/context'
// eslint-disable-next-line import/no-named-default
import { default as SourceContext } from '@/pages/Transformer/components/SourcePanel/context'
import { getViewPort, mapNodes } from '@/utils/utils'
import request from '@/utils/request'
import SquareButton from '@/components/SquareButton'
import useModal from '@/hooks/UseModal'
import { SmallFormLayout } from '@/layouts/Form'
import CodeEditor from '@/components/CodeEditor'
import IconFont from '@/components/IconFont'
import Space from '@/components/Space'
import useCopyToClipboard from '@/hooks/useCopyToClipboard'
import { renderPath } from '@/components/PathInput'
import { getCurrentTenantId } from '@/utils/user'

const { Expandable } = renderers
const { Text } = Typography

function isActived(nodePath, targetNodePath) {
  return isString(nodePath) && isEqual(nodePath, targetNodePath)
}

function stringToJson(ref, format) {
  const editor = ref.current

  if (editor.hasError()) {
    message.error(`不是合法的${format}格式`)
    return Promise.reject()
  }

  return request.post('/transform/parseTargetTree', {
    data: { format, message: editor.getValue() },
  })
}

function stringToJson2(ref, sourceTree, targetTree, sourceFormat, targetFormat) {
  const editor = ref.current

  if (editor.hasError()) {
    message.error(`不是合法的${sourceFormat}格式`)
    return Promise.reject()
  }

  return request('/transform/run', {
    data: { sourceFormat, sourceMessage: editor.getValue(), targetFormat, sourceTree, targetTree },
  })
}

function getHeight() {
  const { height } = getViewPort(false)
  const $header = document.getElementById('layout-header')
  const $tabsNav = document.querySelector('.layout-tabs-nav')

  return (
    height
    - ($header === null ? 0 : $header.offsetHeight)
    - ($tabsNav === null ? 0 : $tabsNav.offsetHeight)
    - 16 * 2
    - 48
    - 12 * 2
    - 37
  )
}

const height = getHeight()

function TargetEditor({ data, form, showMask, hideMask, reloadPage }) {
  const { getFieldDecorator, getFieldValue, validateFields } = form
  const { state = {}, dispatch } = useContext(Context)
  const { state: Tstate = {}, dispatch: Tdispatch } = useContext(TransformerContext)
  const { state: Sstate = {} } = useContext(SourceContext)
  const [_state, copyToClipboard] = useCopyToClipboard()

  const [visible, setVisible] = useState(false)

  const [targetMessage, setTargetMessage] = useState()

  const [show, setShow] = useState(false)

  const importRef = useRef()

  const testRef = useRef()

  const resultRef = useRef()

  const { visible: fromCodeModalVisible, showModal, hideModal } = useModal()
  const [fromCodeModalLoading, setFromCodeModalLoading] = useState(false)

  // 展示目标配置面板
  const showTargetPanel = useCallback(
    (data) => {
      const tree = serialize([data], false)[0]
      dispatch({
        type: 'set_the_whole_tree',
        payload: { data: tree },
      })
    },
    [dispatch],
  )

  // 展示默认目标配置面板
  const showDefaultTargetPanel = useCallback(() => {
    const data = {
      nodeType: 'Object',
      nodeName: 'root',
      // 该值用于标识，当点击节点`配置`时，读取groups的哪个副本。当前分支下副本的添加顺序，从0开始。
      // 这个值并不是对应副本在groups中的下标。因为groups中包含了所有父分支下的副本。而是过滤`getCurrentGroups(groups)`后新数组的下标
      // 点击`切换`时，会引起该变化。同时，子节点的groupIndex重置为0（即让该节点下的所有子节点默认切换到当前分支下的第一个副本）
      groupIndex: 0,
      // 该值由2个部分（动态的）组成[...parent.prefix, parent.groupIndex]
      prefix: [],
      // 当前节点的所有副本
      groups: [
        {
          groupName: '0',
          indexes: [0], // 由2个部分组成[...prefix, 父级副本下当前副本添加的顺序]
        },
      ],
    }
    showTargetPanel(data)
  }, [showTargetPanel])

  const { loading, run } = useRequest(() => stringToJson(importRef, Tstate.targetFormat), {
    manual: true,
    onSuccess: (data) => {
      setVisible(false)

      if (isPlainObject(data))
        showTargetPanel(data)

      else
        message.error('预期返回约定格式的对象')
    },
  })

  const { loading: loading2, run: run2 } = useRequest(
    () => stringToJson2(testRef, Sstate.data, state.data, Tstate.sourceFormat, Tstate.targetFormat),
    {
      manual: true,
      onSuccess: (resultData) => {
        const { targetMessage } = resultData
        setTargetMessage(targetMessage)
      },
    },
  )

  function handleSave(data) {
    const { versionId, sourceTree, targetTree } = data

    // if (!hasChildren(targetTree) && !get(targetTree, 'groups[0].rules[0]')) {
    //   message.error('无效的目标报文');
    //   return Promise.reject();
    // }
    return request.post('/transform/saveVersionData', {
      data: {
        versionId,
        sourceTree,
        targetTree,
        sourceMessage: Tstate.sourceMessage,
      },
    })
  }

  const { loading: loading3, run: save } = useRequest(
    () =>
      handleSave({ versionId: Tstate.versionId, sourceTree: Sstate.data, targetTree: state.data }),
    {
      manual: true,
      onSuccess: () => {
        message.success('保存成功')
      },
    },
  )

  useDeepCompareEffect(() => {
    if (data !== undefined && !isPlainObject(data))
      throw new Error('Invalid `props.data`! it must be a object')

    if (isPlainObject(data))
      dispatch({ type: 'set_the_whole_tree', payload: { data: serialize([data], false)[0] } })
  }, [data])

  function generateTreeFromCode() {
    validateFields((err, _values) => {
      if (err)
        return
      const javaClassName = getFieldValue('javaClassName')
      setFromCodeModalLoading(true)
      request
        .post(`/transform/parseTargetTreeFromCode?className=${javaClassName}`)
        .then((data) => {
          hideModal()
          showTargetPanel(data)
        })
        .finally(() => setFromCodeModalLoading(false))
    })
  }

  const handleExportData = () => {
    const formElement = document.createElement('form')
    formElement.style.display = 'display:none'
    formElement.method = 'post'
    formElement.action = `v1/transform/exportData?tenantId=${getCurrentTenantId()}`
    formElement.target = '_blank'

    const config = document.createElement('input')
    config.type = 'hidden'
    config.name = 'config'
    config.value = JSON.stringify({
      versionId: Tstate.versionId,
      sourceTree: Sstate.data,
      targetTree: state.data,
      sourceMessage: Tstate.sourceMessage,
    })

    formElement.appendChild(config)
    document.body.appendChild(formElement)

    formElement.submit()

    formElement.removeChild(config)
    document.body.removeChild(formElement)
  }

  const { showModal: showTip, hideModal: hideTip, visible: tipVisible } = useModal()

  const props = {
    beforeUpload: (file) => {
      hideTip()
      const formData = new FormData()
      formData.append('file', file)
      formData.append('versionId', Tstate.versionId)
      showMask()
      request
        .post('/transform/importData', {
          data: formData,
          requestType: 'form',
        })
        .then(() => {
          message.success('导入成功')
          reloadPage()
        })
        .finally(hideMask)
      return false
    },
    showUploadList: false,
    accept: '.json',
  }

  const filterNodes = (nodes) => {
    const isPreviewMode = Tstate.previewMode
    return mapNodes(nodes, (n) => {
      if (isPreviewMode && !hasRules(n))
        return null

      return n
    })
  }

  const [isSearching, setIsSearching] = useState(false)

  useEffect(() => {
    const searchInput = document.querySelector(`#target-editor-${Tstate.transformId}-${Tstate.versionId} > div.${classnames(styles.body)} > div > div > input`)
    const handleSearchInputChange = (event) => {
      const newValue = event.target.value
      setIsSearching(!!newValue)
    }

    searchInput && searchInput.addEventListener('keyup', handleSearchInputChange)

    return () => {
      searchInput && searchInput.removeEventListener('keyup', handleSearchInputChange)
    }
  })

  return (
    <div id={`target-editor-${Tstate.transformId}-${Tstate.versionId}`} className={styles.jsonEditor}>
      <div className={classnames(styles.toolbar, 'clearfix')}>
        <div className="ml-2 pull-left main-title" style={{ lineHeight: '40px' }}>
          目标报文树
        </div>
        <div className="pull-right">
          <Tooltip title={Tstate.previewMode ? '关闭预览模式' : '预览模式'}>
            <Icon
              type={Tstate.previewMode ? 'eye-invisible' : 'eye'}
              className={styles.command}
              onClick={() => Tdispatch({ type: 'toggle_preview_mode' })}
            />
          </Tooltip>
          <Divider type="vertical" />

          <Tooltip title="清空目标报文">
            <IconFont
              type="icon-cache"
              className={styles.command}
              style={{
                fontSize: 18,
                paddingLeft: 14.5,
                paddingRight: 14.5,
                paddingTop: 11,
                paddingBottom: 13,
                verticalAlign: 'top',
              }}
              onClick={() => {
                dispatch({
                  type: 'set_the_whole_tree',
                  payload: { data: null },
                })
                message.success('清空成功')
              }}
            />
          </Tooltip>
          <Divider type="vertical" />
          <Tooltip title="导入目标报文">
            <Icon type="upload" className={styles.command} onClick={() => setVisible(true)} />
          </Tooltip>
          <Divider type="vertical" />
          <Tooltip title={state.globalToggle.status ? '全部收起' : '全部展开'}>
            <Icon
              type={state.globalToggle.status ? 'minus-square' : 'plus-square'}
              className={styles.command}
              onClick={() =>
                dispatch({ type: 'open_or_close_node', payload: !state.globalToggle.status })
              }
            />
          </Tooltip>

          {Tstate.readOnly
            ? null
            : (
            <>
              <Divider type="vertical" />
              <Tooltip title="导入配置文件">
                <Icon type="cloud-upload" className={styles.command} onClick={showTip} />
              </Tooltip>
            </>
              )}

          <Divider type="vertical" />
          <Tooltip title="导出配置文件">
            <Icon type="cloud-download" className={styles.command} onClick={handleExportData} />
          </Tooltip>

          {Tstate.readOnly
            ? null
            : (
            <>
              <Divider type="vertical" />
              <Tooltip title="保存">
                {loading3
                  ? (
                  <Icon type="loading" className={styles.command} />
                    )
                  : (
                  <Icon
                    type="save"
                    className={styles.command}
                    onClick={() => {
                      save()
                    }}
                  />
                    )}
              </Tooltip>
            </>
              )}

          <Divider type="vertical" />
          <Tooltip title="测试">
            <Icon type="deployment-unit" className={styles.command} onClick={() => setShow(true)} />
          </Tooltip>
        </div>
      </div>
      <div className={classnames(styles.body)} style={{ height: height + 37 + 32 + 12 }}>
        {!state.data ? (
          <div
            style={{
              height: '90%',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
            }}
          >
            <SquareButton
              onClick={() => setVisible(true)}
              style={{ marginRight: '16px' }}
              icon="upload"
              text="导入目标报文"
            />
            <SquareButton
              style={{ marginRight: '16px' }}
              onClick={showDefaultTargetPanel}
              icon="plus"
              text="手动创建"
            />
            <SquareButton onClick={showModal} icon="code" text="从Java类生成" />
          </div>
        ) : (
          <FilteringContainer nodes={filterNodes([state.data])} indexSearch={TreeFilter}>
            {({ nodes }) => (
              <ul className={styles.wtree} style={{ height }}>
                <Tree
                  nodeMarginLeft={20}
                  nodes={nodes}
                  onChange={nodes =>
                    dispatch({ type: 'set_the_whole_tree', payload: { data: nodes[0] } })
                  }
                >
                  {({ style, node, ...rest }) => {
                    const nodePath = getAttrPath([state.data], k => k.id === node.id)
                    const parent = getNodeById([state.data], last(node.parents))

                    const handleSelect = (e) => {
                      const { target } = e
                      if (target && target.classList.contains('tgIcon'))
                        return

                      e.stopPropagation()

                      const targetNodePath = nodePath

                      if (targetNodePath === null || targetNodePath === undefined)
                        throw new Error(`目标报文${node.nodeName}节点缺少nodePath`)

                      const sourceNodePath = get(Tstate, 'sourceNodePath')

                      // 凑齐一对
                      if (
                        Array.isArray(sourceNodePath)
                        && targetNodePath !== null
                        && targetNodePath !== undefined
                      ) {
                        Tdispatch({
                          type: 'quick_mapping_record_target',
                          payload: {
                            targetNodePath,
                          },
                        })

                        dispatch({
                          type: 'quick_mapping',
                          payload: {
                            targetNodePath,
                            sourceNodePath,
                            current: node,
                            parent,
                          },
                        })
                      }
                    }

                    return (
                      <Dropdown
                        trigger={['contextMenu']}
                        overlay={
                          <Menu>
                            <Menu.Item
                              onClick={() => {
                                message.success(`拷贝路径: ${node.nodeName}`)
                                copyToClipboard(renderPath(node.nodePath))
                              }}
                            >
                              <Icon type="copy" />
                              拷贝节点路径
                            </Menu.Item>
                          </Menu>
                        }
                      >
                        <div
                          style={{
                            ...style,
                            paddingLeft: style.marginLeft + 24,
                            marginLeft: 0,
                          }}
                          className={classnames(styles.row, {
                            [styles.actived]: isActived(
                              getAttrPath([state.data], k => k.id === node.id),
                              get(Tstate, 'targetNodePath'),
                            ),
                          })}
                          onClick={handleSelect}
                        >
                          <Expandable
                            node={node}
                            {...rest}
                            iconsClassNameMap={{
                              expanded: `tgIcon icon-arrow-down ${styles.switcher}`,
                              collapsed: `tgIcon icon-arrow-right ${styles.switcher}`,
                              // lastChild: 'mi mi-insert-drive-file',
                            }}
                          >
                            <Node
                              handleSelect={handleSelect}
                              nodePath={nodePath}
                              current={node}
                              parentId={last(node.parents)}
                              parent={parent}
                              offset={style.marginLeft}
                              disableAddNode={isSearching}
                            />
                          </Expandable>
                        </div>
                      </Dropdown>
                    )
                  }}
                </Tree>
              </ul>
            )}
          </FilteringContainer>
        )}
      </div>

      <Modal
        visible={tipVisible}
        onCancel={hideTip}
        onOk={hideTip}
        width={416}
        bodyStyle={{ padding: '32px 32px 24px' }}
        footer={null}
      >
        <div className="ant-modal-confirm-body-wrapper">
          <div className="ant-modal-confirm-body">
            <Icon type="question-circle" className="text-warning" />
            <span className="ant-modal-confirm-title">你确定要导入配置吗？</span>
            <div className="ant-modal-confirm-content">
              当前的配置将会被完全覆盖掉，并且无法恢复！
            </div>
          </div>
          <Space className="pull-right" style={{ marginTop: 24 }}>
            <Button onClick={hideTip}>取 消</Button>
            <Upload {...props} className={styles.upload}>
              <Button type="primary">导 入</Button>
            </Upload>
          </Space>
        </div>
      </Modal>

      <Modal
        title="从Java类生成"
        width={550}
        visible={fromCodeModalVisible}
        onOk={() => generateTreeFromCode()}
        okButtonProps={{
          loading: fromCodeModalLoading,
        }}
        onCancel={hideModal}
        destroyOnClose
      >
        <Form.Item label="Java类路径" {...SmallFormLayout}>
          {getFieldDecorator('javaClassName', {
            rules: [
              {
                required: true,
                message: '类路径不能为空',
              },
            ],
          })(
            <Input
              allowClear
              placeholder="com.iwhalecloud.tangram.standalone.example.OrderQueryDTO"
              addonAfter={
                <Text
                  className={styles.copyable}
                  copyable={{ text: getFieldValue('javaClassName') }}
                />
              }
            />,
          )}
        </Form.Item>
      </Modal>

      <Modal
        visible={visible}
        width={780}
        title={`导入${Tstate.targetFormat}`}
        onOk={() => run()}
        onCancel={() => setVisible(false)}
        destroyOnClose
        okButtonProps={{
          loading,
        }}
      >
        <CodeEditor ref={importRef} mode={Tstate.targetFormat} />
      </Modal>

      <Modal
        title="测试"
        visible={show}
        width={1100}
        onOk={() => run2()}
        okText="开始测试"
        onCancel={() => {
          setShow(false)
          setTargetMessage()
        }}
        destroyOnClose
        okButtonProps={{
          loading: loading2,
        }}
      >
        <Form>
          <Row gutter={16}>
            <Col span={12}>
              <Form.Item
                label={
                  <>
                    <span className="bold">{Tstate.sourceFormat}</span> 源报文
                  </>
                }
              >
                <CodeEditor
                  ref={testRef}
                  data={
                    Tstate.sourceMessage
                    || (Tstate.sourceType === 'VALIDATE_FAILURE'
                      ? JSON.stringify(validationResult)
                      : undefined)
                  }
                  mode={Tstate.sourceFormat}
                  height={500}
                />
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item
                label={
                  <>
                    <span className="bold">{Tstate.targetFormat}</span> 目标报文
                  </>
                }
              >
                <CodeEditor
                  ref={resultRef}
                  data={targetMessage}
                  mode={Tstate.targetFormat}
                  height={500}
                />
              </Form.Item>
            </Col>
          </Row>
        </Form>
      </Modal>
    </div>
  )
}

export default Form.create()(TargetEditor)
